package com.fujimana.rockincat.restree.proxy;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Set;

import com.fujimana.rockincat.restree.annotation.rest.Bind;
import com.fujimana.rockincat.restree.annotation.rest.Endpoint;
import com.fujimana.rockincat.restree.annotation.rest.Handle;
import com.fujimana.rockincat.restree.annotation.rest.Override;
import com.fujimana.rockincat.restree.annotation.rest.Resource;
import com.fujimana.rockincat.restree.context.Context;
import com.fujimana.rockincat.restree.exception.MetaDataParseException;

public class ResourceMetaDataFactory {

	private static final ResourceMetaDataFactory INSTANCE = new ResourceMetaDataFactory();

	private ResourceMetaDataFactory() {
	}

	public static final ResourceMetaDataFactory getInstance() {
		return INSTANCE;
	}

	public ResourceMetaData getMetaData(Class<?> clazz,
			Set<MethodMetaData> methods) {

		ResourceMetaData ret = new ResourceMetaData();

		Resource rs = clazz.getAnnotation(Resource.class);

		if (rs == null) {
			throw new MetaDataParseException("TODO");
		}

		ret.setTarget(clazz);
		if (rs.child().length != 0) {
			ret.setChildResource(rs.child());
		}
		ret.setPath(rs.path());

		ret.setEndpoint(clazz.getAnnotation(Endpoint.class) != null);

		for (Method m : clazz.getMethods()) {

			if (m.getAnnotation(Handle.class) != null) {
				if (m.getParameterTypes().length != 1) {
					continue;
				}

				if (Context.class.isAssignableFrom(m.getParameterTypes()[0]) == false) {
					continue;
				}

				ret.setHandleMethod(m);
			}
		}

		ret.setParamTypes(new HashMap<String, BindMetaData>());
		ret.setInvokeMethods(new HashMap<String, Method>());
		for (Method m : clazz.getMethods()) {
			for (Annotation a : m.getAnnotations()) {

				if (isHttpMethodAnnotation(a, methods)) {
					Bind bind = m.getAnnotation(Bind.class);
					if (bind != null) {
						ret.getParamTypes().put(a.annotationType().getName(),
								new BindMetaData(bind.target(), bind.name()));
					}

					ret.getInvokeMethods().put(a.annotationType().getName(), m);
					break;
				}
			}
		}

		int precedence = 0;
		String key = clazz.getName();

		Override override = clazz.getAnnotation(Override.class);
		if (override != null) {
			precedence = override.precedence().getValue();
			key = override.getClass().getName();
		}

		ret.setPrecedence(precedence);
		ret.setKey(key);

		return ret;
	}

	private boolean isHttpMethodAnnotation(Annotation annotation,
			Set<MethodMetaData> methods) {

		for (MethodMetaData metaData : methods) {
			if (annotation.annotationType().getName().equals(
					metaData.getAnnotatin().getName())) {
				return true;
			}
		}

		return false;
	}

}
